package com.lhj.library.application.config;

import cn.dev33.satoken.context.SaHolder;
import cn.dev33.satoken.exception.NotLoginException;
import cn.dev33.satoken.exception.NotRoleException;
import cn.dev33.satoken.interceptor.SaInterceptor;
import cn.dev33.satoken.router.SaRouter;
import cn.dev33.satoken.stp.StpUtil;
import cn.dev33.satoken.util.SaResult;
import com.fasterxml.jackson.annotation.JsonInclude;
import com.fasterxml.jackson.databind.ObjectMapper;
import com.fasterxml.jackson.databind.SerializationFeature;
import com.lhj.library.common.constant.RoleConstant;
import com.lhj.library.common.entity.Result;
import org.springframework.context.annotation.Configuration;
import org.springframework.http.converter.HttpMessageConverter;
import org.springframework.http.converter.json.MappingJackson2HttpMessageConverter;
import org.springframework.web.bind.annotation.ExceptionHandler;
import org.springframework.web.bind.annotation.RestControllerAdvice;
import org.springframework.web.servlet.config.annotation.InterceptorRegistry;
import org.springframework.web.servlet.config.annotation.WebMvcConfigurationSupport;
import org.springframework.web.servlet.config.annotation.WebMvcConfigurer;

import java.util.List;

/**
 * mvc的全局处理
 *
 * @author: lhj
 * @date: 2023/10/7
 */
@Configuration
public class GlobalConfig extends WebMvcConfigurationSupport {

    // 注册 Sa-Token 的拦截器

    @Override
    public void addInterceptors(InterceptorRegistry registry) {
        // 注册路由拦截器，自定义认证规则
        registry.addInterceptor(new SaInterceptor(handler -> {

            // 登录校验 -- 拦截所有路由，并排除/auth/login 用于开放登录 /auth/register 用于注册
            // 指定一条 match 规则
            SaRouter
                    .match("/**")    // 拦截的 path 列表，可以写多个 */
                    .notMatch("/auth/login")
                    .notMatch("/auth/register")// 排除掉的 path 列表，可以写多个
                    .check(r -> StpUtil.checkLogin());        // 要执行的校验动作，可以写完整的 lambda 表达式

            // 权限校验 -- 不同模块校验不同权限
            SaRouter.match("/book/select", r -> StpUtil.checkRoleOr(RoleConstant.NORMAL_USER, RoleConstant.ADMIN_USER));
            SaRouter.match("/book/selectByPage", r -> StpUtil.checkRoleOr(RoleConstant.NORMAL_USER, RoleConstant.ADMIN_USER));
            SaRouter.match("/book/borrow", r -> StpUtil.checkRoleOr(RoleConstant.NORMAL_USER, RoleConstant.ADMIN_USER));
            SaRouter.match("/book/returned", r -> StpUtil.checkRoleOr(RoleConstant.NORMAL_USER, RoleConstant.ADMIN_USER));
            SaRouter.match("/record/selectByPage", r -> StpUtil.checkRoleOr(RoleConstant.NORMAL_USER, RoleConstant.ADMIN_USER));
            // 根据 path 路由匹配   ——— 支持写多个path，支持写 restful 风格路由
            // 功能说明: 使用 /user , /goods 或者 /art/get 开头的任意路由都将进入 check 方法
            SaRouter.match("/book/add",
                    "/book/update",
                    "/book/delete",
                    "/user/**").check(r -> StpUtil.checkRoleOr(RoleConstant.ADMIN_USER));
        })).addPathPatterns("/**");
    }

    @Override
    protected void configureMessageConverters(List<HttpMessageConverter<?>> converters) {
        super.configureMessageConverters(converters);
        converters.add(mappingJackson2HttpMessageConverter());
    }

    /**
     * 自定义mappingJackson2HttpMessageConverter
     * 目前实现：空值忽略，空字段可返回
     */
    private MappingJackson2HttpMessageConverter mappingJackson2HttpMessageConverter() {
        ObjectMapper objectMapper = new ObjectMapper();
        objectMapper.configure(SerializationFeature.FAIL_ON_EMPTY_BEANS, false);
        objectMapper.setSerializationInclusion(JsonInclude.Include.NON_NULL);
        return new MappingJackson2HttpMessageConverter(objectMapper);
    }


    @RestControllerAdvice
    public class GlobalExceptionHandler {
        // 拦截：未登录异常
        @ExceptionHandler(NotLoginException.class)
        public Result handlerException(NotLoginException e) {
            // 打印堆栈，以供调试
            e.printStackTrace();
            // 返回给前端
            return Result.fail(e.getMessage());
        }

        // 拦截：缺少角色异常
        @ExceptionHandler(NotRoleException.class)
        public Result handlerException(NotRoleException e) {
            e.printStackTrace();
            return Result.fail("缺少角色：" + e.getRole());
        }
    }

    // 拦截：其它所有异常
    @ExceptionHandler(Exception.class)
    public Result handlerException(Exception e) {
        e.printStackTrace();
        return Result.fail(e.getMessage());
    }
}

